/**
 * 加载器
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import { toPromise } from './data';

const ASYNC_LOCK_LOAD: any = {};
const MERGE_LOAD: any = {};

/**
 * 同步锁加载器，相同name的loader只执行一次
 * 场景：比如远程加载同一个名字的数据时结果都是一样的所以无需发送多次请求
 * @param name 分组名，相同的分组 loader只会执行一次
 * @param loader 加载器，加载数据的方法
 * @param handler
 */
export function syncLockLoad(name: string, loader: Function, handler?: Function) {
  // @ts-ignore
  let data = ASYNC_LOCK_LOAD[name];
  if (!data) {
    data = [];
    ASYNC_LOCK_LOAD[name] = data;
    toPromise(loader(name))
      .then((res) => {
        if (typeof handler === 'function') {
          res = handler(res);
        }
        const promises = ASYNC_LOCK_LOAD[name];
        delete ASYNC_LOCK_LOAD[name];
        for (const x of promises) {
          x.resolve(res);
        }
      })
      .catch((err) => {
        const promises = ASYNC_LOCK_LOAD[name];
        delete ASYNC_LOCK_LOAD[name];
        for (const x of promises) {
          x.reject(err);
        }
      });
  }
  return new Promise((resolve, reject) => {
    data.push({ resolve, reject });
  });
}

/**
 * 合并加载
 * 场景: 根据code获取数据字典，通过把一定时间内需要获取的数据字典合并为一个请求，后端响应后再分发（需后端提供多个code的获取接口）
 * @param params 加载参数
 * @param name 合并分组名称
 * @param loader 加载方法，同一个分组名称（name）的一定时间内只执行一次
 * @param responseHandler 响应数据处理，加载回来的数据可以在这里分发
 * @param ms 等待多久执行，等待的这段时间，相同组的加载都会进行合并，时间到执行 loader 方法
 */
export function mergeLoad({
  params,
  name,
  loader,
  responseHandler = (res: any, p: any, i: any) => res[i],
  ms,
}: {
  /**
   * 合并分组名称，区分合并的分组
   */
  name: string;
  /**
   * 参数（组内各成员独立参数）
   */
  params: any;
  /**
   * 加载方法，同一个分组名称（name）的一定时间内只执行一次
   */
  loader: Function;
  /**
   * 响应后的数据拆分
   */
  responseHandler?: Function;
  /**
   * 等待多久执行，等待的这段时间，相同组的加载都会进行合并，时间到执行 loader 方法
   */
  ms?: number;
}) {
  let data = MERGE_LOAD[name];
  if (!data) {
    data = [];
    MERGE_LOAD[name] = data;
    setTimeout(() => {
      toPromise(loader(data.map((x: any) => x.params)))
        .then((res) => {
          const promises = MERGE_LOAD[name];
          delete MERGE_LOAD[name];
          for (let i = 0; i < promises.length; i++) {
            const x = promises[i];
            x.resolve(responseHandler(res, x.params, i));
          }
        })
        .catch((err) => {
          const promises = MERGE_LOAD[name];
          delete MERGE_LOAD[name];
          for (let i = 0; i < promises.length; i++) {
            const x = promises[i];
            x.reject(err);
          }
        });
    }, ms ?? 300);
  }
  return new Promise((resolve, reject) => {
    data.push({ params, resolve, reject });
  });
}

/**
 * 函数防抖(调用时不会立即触发,延时指定时间后触发,延时时间内再次调用会重新计算延时)
 * @param {Function} func 目标方法
 * @param {Number} delay 防抖延时
 * @param params 参数
 */
// eslint-disable-next-line consistent-return
export function debounce(func: Function, delay = 800, ...params: any[]) {
  const call = (...params: any[]) => {
    // @ts-ignore
    clearTimeout(func.timer);
    // @ts-ignore
    func.timer = setTimeout(() => func(...params), delay);
  };
  if (arguments.length <= 2) {
    return call;
  }
  call(...params);
}

/**
 * 函数节流(多次调用,在节流间隔时间内只触发一次,最后一次调用一定会触发)
 * @param {Function} func 目标方法
 * @param {Number} interval 节流间隔
 * @param params 参数
 */
// eslint-disable-next-line consistent-return
export function throttle(func: Function, interval = 800, ...params: any[]) {
  // @ts-ignore
  func.$throttle_delay = interval;
  const call = (...params: any[]) => {
    // @ts-ignore
    func.$throttle_params = params;
    // @ts-ignore
    if (!func.$throttle_timer) {
      // @ts-ignore
      func.$throttle_timer = setTimeout(() => {
        // @ts-ignore
        func(...func.$throttle_params);
        // @ts-ignore
        delete func.$throttle_params;
        // @ts-ignore
        clearTimeout(func.$throttle_timer);
        // @ts-ignore
        delete func.$throttle_timer;
        // @ts-ignore
      }, func.$throttle_delay);
    }
  };

  if (arguments.length <= 2) {
    return call;
  }
  call(...params);
}
